package com.dylan.历史.多线程.并发编程的艺术.编程基础;

import com.dylan.历史.util.LogUtil;
import com.dylan.历史.util.SleepUtils;
import org.junit.Test;

/**
 * Created by Dylan on 2018/7/13.
 */
public class 线程安全中断 {

    /**
     * 展示安全退出线程的两种方式
     * 1. 使用interrupt()设置中断标识位，然后用isInterrupted()判断中断标识位
     * 2. 使用自定义变量
     */
    @Test
    public void test1() {
        Runner one = new Runner();
        Thread t1 = new Thread(one);
        t1.start();
        LogUtil.info(t1.getName() + " : " + t1.getState());
        SleepUtils.seconds(1);
        // 使用中断标识位让线程退出
        t1.interrupt();
        SleepUtils.seconds(1);
        LogUtil.info(t1.getName() + " : " + t1.getState());

        LogUtil.logCuttingLine();

        Runner two = new Runner();
        Thread t2 = new Thread(two);
        t2.start();
        LogUtil.info(t2.getName() + " : " + t2.getState());
        SleepUtils.seconds(1);
        two.cancel(); // 使用自定义变量让线程退出
        SleepUtils.seconds(1);
        LogUtil.info(t2.getName() + " : " + t2.getState());
    }

    /**
     * 展示interrupt()设置中断标识位后，响应中断的方法抛出了异常并清除了标识位。
     * 捕获异常后，如果不处理，线程会继续运行
     */
    @Test
    public void test2() {
        Runner2 one = new Runner2();
        Thread t1 = new Thread(one);
        t1.start();
        LogUtil.info(t1.getName() + " : " + t1.getState());
        SleepUtils.seconds(1);
        // 设置中断标识位。然后sleep()方法抛出InterruptedException，且清除了中断标识位。但是捕获异常后没有处理，所以线程并没有中断
        t1.interrupt();
        SleepUtils.seconds(1);
        LogUtil.info(t1.getName() + " : 中断标识位 : " + t1.isInterrupted());
        LogUtil.info(t1.getName() + " : " + t1.getState()); // TIMED_WAITING

        Runner2 two = new Runner2();
        Thread t2 = new Thread(two);
        t2.start();
        LogUtil.info(t2.getName() + " : " + t2.getState());
        SleepUtils.seconds(1);
        two.cancel(); // 线程退出
        SleepUtils.seconds(1);
        LogUtil.info(t2.getName() + " : " + t2.getState()); // TERMINATED
    }

    class Runner implements Runnable {
        private int i;
        private volatile boolean on = true;

        @Override
        public void run() {
            while (on && !Thread.currentThread().isInterrupted()) {
                i++;
            }
            LogUtil.info("计算结束: Count i=" + i);
        }

        public void cancel() {
            on = false;
        }
    }

    class Runner2 implements Runnable {
        private int i;
        private volatile boolean on = true;

        @Override
        public void run() {
            while (on && !Thread.currentThread().isInterrupted()) {
                i++;
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    LogUtil.error(e);
                }
            }
            LogUtil.info("计算结束: Count i=" + i);
        }

        public void cancel() {
            on = false;
        }
    }
}
